﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

public class CowsAndBulls
{
    private string number;
    private int atempts;
    private int helpsUsed;
    private bool[] digitsShown;
    private SortedDictionary<int, List<string>> topBoard;
    private bool gameEnded;
    
    public CowsAndBulls()
    {
        this.topBoard = new SortedDictionary<int, List<string>>();
    }

    public void Play()
    {
        Random rand = new Random();

        this.atempts = 0;
        this.helpsUsed = 0;
        this.number = "1123"; //rand.Next(0, 10000).ToString().PadLeft(4, '0');
        this.digitsShown = new bool[4];
        this.gameEnded = false;
        Console.WriteLine("\nWelcome to “Bulls and Cows” game. Please try to guess my secret 4-digit number.\n" +
                          "Use 'top' to view the top scoreboard, 'restart' to start a new game and 'help'\n" +
                          "to cheat and 'exit' to quit the game.");

        string command = "";
        while (command != "exit")
        {
            if (!this.gameEnded)
            {
                Console.Write("Enter your guess or command: ");
                command = Console.ReadLine();
            }
            else
            {
                command = "restart";
            }
            switch (command)
            {
                case "top":
                    this.PrintTopScore();
                    break;
                case "restart":
                    this.Play();
                    command = "exit";
                    break;
                case "help":
                    this.helpsUsed++;
                    this.Help();
                    break;
                case "exit":
                    break;
                default:
                    if (this.IsItANumber(command))
                    {
                        this.atempts++;
                        this.CheckForBullsAndCows(command);
                    }
                    else
                    {
                        Console.WriteLine("Invalid number or command.");
                    }
                    break;
            }
        }
    }

    public bool IsItANumber(string command)
    {
        if (command != null && command.Length == 4)
        {
            bool correctNumber = true;
            for (int i = 0; i < 4; i++)
            {
                if (command[i] < '0' || command[i] > '9')
                {
                    correctNumber = false;
                }
            }
            return correctNumber;
        }
        else
        {
            return false;
        }
    }

    public void CheckForBullsAndCows(string number)
    {
        int cows = 0;
        int bulls = 0;
        bool[] used = new bool[4];

        for (int i = 0; i < 4; ++i)
        {
            for (int j = 0; j < 4; ++j)
            {
                if (this.number[i] == number[j])
                {
                    if (i == j)
                    {
                        if (used[j])
                        {
                            bulls++;
                            cows--;
                        }
                        else
                        {
                            bulls++;
                        }
                    }
                    else
                    {
                        if (!used[j])
                        {
                            cows++;
                        }
                    }
                    used[j] = true;
                }
            }
        }

        this.Print(cows, bulls);
    }

    public void Print(int cows, int bulls)
    {
        if (bulls == 4 && cows == 0)
        {
            if (this.helpsUsed == 0)
            {
                Console.Write("Congratulations! You guessed the secret number in {0} attempts." +
                                    "Please enter your\nname for the top scoreboard: ", this.atempts);
                string name = Console.ReadLine();
                if (this.topBoard.ContainsKey(this.atempts))
                {
                    this.topBoard[this.atempts].Add(string.Format("{0} --> {1} guesses", name, this.atempts));
                }
                else
                {
                    this.topBoard.Add(this.atempts, new List<string>() {string.Format("{0} --> {1} guesses", name, this.atempts)});
                }
                this.PrintTopScore();
                this.gameEnded = true;
            }
            else
            {
                Console.WriteLine("Congratulations! You guessed the secret number in {0} attempts and {1} cheats." +
                                  "You\nare not allowed to enter the top scoreboard.", this.atempts, this.helpsUsed);
                this.PrintTopScore();
                this.gameEnded = true;
            }
        }
        else
        {
            Console.WriteLine("Wrong number! Bulls: {0}, Cows: {1}", bulls, cows);
        }
    }

    public void PrintTopScore()
    {
        int count = 0;
        foreach (var score in this.topBoard)
        {
            if (count > 4)
            {
                break;
            }
            foreach (var name in score.Value)
            {
                if (count > 4)
                {
                    break;
                }
                Console.WriteLine("{0}. {1}", ++count, name);   
            }
        }
    }

    public void Help()
    {
        Random rand = new Random();
        int digit = rand.Next(0, 4);
        if (this.helpsUsed < 5)
        {
            while (this.digitsShown[digit])
            {
                digit = rand.Next(0, 4);
            }
            this.digitsShown[digit] = true;
        }
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 4; ++i)
        {
            if (this.digitsShown[i])
            {
                sb.Append(this.number[i]);
            }
            else
            {
                sb.Append("X");
            }
        }

        Console.WriteLine("The number looks like {0}.", sb.ToString());
    }
}

